<?php
/**
 * Debug
 * 
 * @category	Util
 * @package		dayscript.util
 * @subpackage	
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license		
 * @version		1.0
 * @version		$Revision: 0 $
 * @filesource	
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-08-13 $
 */

namespace dayscript\util;

/**
 * Debug
 *
 * Singleton, for debug purposes only
 * 
 * @category	Util
 * @package		dayscript.util
 * @subpackage	
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license		
 * @version		1.0
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		\dayscript\log\LogManager, \ReflectionClass
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-08-13 $
 */
class Debug	{
	/**
	 * Debug constructor
	 * @return Debug
	 */
	private function __construct( )	{
		
	}
	/**
	 * Generates and logs/returns a string representation of the $obj variable value.
	 * Is an shortcut (almost magic) method for dumpVar and dumpObject.
	 * 
	 * @param mixed $obj Element to dump.
	 * @param $return Whether return or not the dump string of the object.
	 * @param boolean $all When $obj is an object it indicates whether or not include non public object's members.
	 * @param boolean $callMethods Whether or not call every each posible object's functions (Obviously it will only call methods without parameters).
	 * @return string|null
	 */
	public static function dump( $obj, $return = false, $all = false, $callMethods = false )	{
		if( \is_object( $obj ) )
			return self::dumpObject( $obj, $all, $callMethods, $return );
		if( \is_array( $obj ) )
			return self::dumpArray( $obj, $return );
			
		return self::dumpVar( $obj, $return );
	}
	/**
	 * Generates and logs/returns a string representation of the variable value.
	 * 
	 * @param mixed $value Element to dump.
	 * @param $return Whether return or not the dump string of the object.
	 * @return string|null
	 */
	public static function dumpVar( $value, $return = false )	{
		$str = '<font color="#003366">' . \gettype( $value ) . '</font>';
		if( \is_array( $value ) )
			$str .= ' <i>{length=' . \count( $value ) . '}</i>';
		else if( \is_string( $value ) )
			$str .= ' <font color="#cc0000">\'' . \htmlentities( $value ) . '\'</font> <i>{length=' . \strlen( $value ) . '}</i>';
		else if( \is_bool( $value ) )
			$str .= ' <font color="#4E069A">' . ( $value ? 'true' : 'false' ) . '</font>';
		else if( \is_object( $value ) )
			$str .= ' <font color="#660066">' . \get_class( $value ) . '( )</font>' . ( $value instanceof \Countable ? ' {' . \count( $value ) . '}' : '' );
		else if( \is_numeric( $value ) )
			$str .= ' (<font color="#4E9A06">' . $value . '</font>)';
		else if( \is_null( $value ) )
			$str = '<font color="#336600">null</font>';
		else
			$str .= ' \'' . $value . '\'';	// Unknown case
		
		if( !$return && \dayscript\log\LogManager::canLog( \dayscript\log\LogManager::LOG_USER_INFO ) )	{
			echo $str;
			return null;
		}
		return $str;
	}
	/**
	 * Generates and logs/returns a string representation of the array values.
	 * 
	 * @param array $array Element to dump.
	 * @param $return Whether return or not the dump string of the array.
	 * @return string|null
	 */
	public static function dumpArray( $array, $return = false )	{
		if( !\is_array( $array ) )	{
			\dayscript\log\LogManager::log( "Debug dumpArray argument must be an array.", \dayscript\log\LogManager::LOG_USER_INFO );
			return;
		}
		
		$tabs = 0;
		$str = self::strIdent( $tabs ) . '<br />' .
				self::strIdent( $tabs ) . '<font color="#660066"><b>' . \gettype( $array ) . '</b> <i>{length=' . \count( $array ) . '}</i></font>' . 
				self::strIdent( $tabs ) . '<ul>';
		
		$recursive = array( 'SDO_Model_PropertyImpl' );
		foreach( $array as $key => $value )
			$str .= self::strIdent( $tabs + 1 ) . '<li>' . $key . ' <font color="#888a85">=&gt;</font> ' . ( \is_object( $value ) && !\in_array( \get_class( $value ), $recursive ) ? self::dumpVar( $value, true ) : self::dump( $value, true, true, true ) ) . '</li>';
		$str .= self::strIdent( $tabs ) . '</ul>';
		
		if( !$return && \dayscript\log\LogManager::canLog( \dayscript\log\LogManager::LOG_USER_INFO ) )	{
			echo $str;
			return null;
		}
		return $str;
	}
	/**
	 * Generates and logs/returns a string representation of the object.
	 * 
	 * @param mixed $obj Object to dump.
	 * @param boolean $all Whether or not include non public object's members.
	 * @param boolean $callMethods Whether or not call every each posible object's functions (Obviously it will only call methods without parameters).
	 * @param $return Whether return or not the dump string of the object.
	 * @return string|null
	 */
	public static function dumpObject( $obj, $all = false, $callMethods = false, $return = false )	{
		if( !\is_object( $obj ) )	{
			\dayscript\log\LogManager::log( "Debug dumpObject argument must be an object.", \dayscript\log\LogManager::LOG_USER_INFO );
			return;
		}
			
		$reflection = new \ReflectionClass( $obj );
		$magicMethod = ( $obj instanceOf \Iterator || $obj instanceOf \ArrayAccess );
		
		$tabs = 0;
		$str = self::strIdent( $tabs ) . '<br />' .
				self::strIdent( $tabs ) . '<font color="#660066"><b>' . \get_class( $obj ) . '</b></font>';
				
		$str .=	self::strIdent( $tabs ) . '<ul>' . 
				self::strIdent( ++$tabs ) . '<li>Methods';
		
		foreach( $reflection->getMethods( ) as $method )	{
			if( $method->isPublic( ) || $all )	{
				$strMethod = '';
				if( $method->isPrivate( ) )
					$strMethod .= '<font color="#000066"><i>private</i></font> ';
				if( $method->isPublic( ) )
					$strMethod .= '<font color="#006600"><i>public</i></font> ';
				if( $method->isProtected( ) )
					$strMethod .= '<font color="#006666"><i>protected</i></font> ';
				if( $method->isFinal( ) )
					$strMethod .= '<font color="#666600"><i>final</i></font> ';
				if( $method->isAbstract( ) )
					$strMethod .= '<font color="#666600"><i>abstract</i></font> ';
				if( $method->isStatic( ) )
					$strMethod .= '<font color="#666600"><i>static</i></font> ';
				if( $method->returnsReference( ) )
					$strMethod .= '&';
				
				$strMethod .= $method->getName( );
				
				$magicMethod = $magicMethod || ( \strpos( $method->getName( ), '__get' ) === 0 || \strpos( $method->getName( ), '__set' ) === 0 || \strpos( $method->getName( ), '__call' ) === 0 );
				
				$params = array( );
				foreach ( $method->getParameters( ) as $param )	{
					$strParam = '';
					if( $param->isOptional( ) )
						$strParam .= '[';
					if( $param->getClass( ) )
						$strParam .= '<font color="#660066">' . $param->getClass( )->getName( ) . '</font> ';
					if( $param->isPassedByReference( ) )
						$strParam .= '&';
					$strParam .= $param->getName( );
					if( $param->isOptional( ) )	{
						if( $param->isDefaultValueAvailable( ) )
							$strParam .= ' = ' . self::dumpVar( $param->getDefaultValue( ), true );
						
						$strParam .= ']';
					}
					$params[] = $strParam;
				}
				$strMethod .= '( ' . \implode(', ', $params ) . ' )';
				$avoid = array( 'clear', 'init', 'reset', 'clean', '__toString' );
				$recursive = array( 'getInstanceProperties' );
				if( $callMethods && $method->isPublic( ) && $method->getNumberOfRequiredParameters( ) == 0 
					&& !$method->isConstructor( ) && !$method->isDestructor( ) && !\in_array( $method->getName( ), $avoid ) )	{
					$value = $method->invoke( $obj );
					$strMethod .= self::strIdent( $tabs + 1 ) . '<ul><li>' . ( !\in_array( $method->getName( ), $recursive ) ? self::dumpVar( $value, true ) : self::dump( $value, true, true, true ) ) . '</li></ul>';
				}
				$str .= self::strIdent( $tabs + 1 ) . '<ul><li>' . \str_replace( '  ', ' ', $strMethod ) . '</li></ul>';
			}
		}
		$str .= self::strIdent( $tabs - 1 ) . '</li>' .
				self::strIdent( $tabs ) . '<li>Properties';
		$properties = $reflection->getProperties( );
		if( $properties )	{
			foreach( $properties as $property )	{
				if( $property->isPublic( ) || $all )	{
					$strProp = '';
					if( $property->isPrivate( ) )
						$strProp .= '<font color="#000066"><i>private</i></font>';
					if( $property->isPublic( ) )
						$strProp .= '<font color="#006600"><i>public</i></font>';
					if( $property->isProtected( ) )
						$strProp .= '<font color="#006666"><i>protected</i></font>';
					if( $property->isStatic( ) )
						$strProp .= ' <font color="#666600"><i>static</i></font>';
					$str .= self::strIdent( $tabs + 1 ) . '<ul><li>' . $strProp . ' ' . $property->getName( ) . self::strIdent( $tabs + 2 ) . ( $property->isPublic( ) ? '<ul><li>' . self::dumpVar( $property->getValue( $obj ), true ) . '</li></ul>' : '' ) . self::strIdent( $tabs + 1 ) . '</li></ul>';
				}
			}
		}
		else	{
			$properties = \get_object_vars( $obj );
			foreach( $properties as $key => $value )	{
				$strProp = '<font color="#006600"><i>public</i></font>';
				$str .= self::strIdent( $tabs + 1 ) . '<ul><li>' . $strProp . ' ' . $key . self::strIdent( $tabs + 2 ) . '<ul><li>' . self::dumpVar( $value, true ) . '</li></ul>' . self::strIdent( $tabs + 1 ) . '</li></ul>';
			}
		}
		
		if( $obj instanceOf \SDO_DataObject )	{
			$str .= self::strIdent( ++$tabs ) . '<li>SDO Properties' . 
					self::strIdent( $tabs ) . '<ul>';
					
			$reflector = new \SDO_Model_ReflectionDataObject( $obj );
			$properties = $reflector->getInstanceProperties( );
			
			foreach( $properties as $property )	{
				$str .= self::strIdent( $tabs ) . '<li><font color="#006600">' . $property->getName( ) . '</font></li>';
				$str .= self::strIdent( ++$tabs ) . '<ul>';
				$str .= self::strIdent( $tabs ) .  '<li><font color="#000066">type:</font> <font color="#660066"><b>' . $property->getType( )->getName( ) . '</b></font></li>';
				$str .= self::strIdent( $tabs ) .  '<li><font color="#000066">namespaceURI:</font> ' . $property->getType( )->getNamespaceURI( ) . '</li>';
				$str .= self::strIdent( $tabs ) .  '<li><font color="#000066">sequenced:</font> ' . ( $property->getType( )->isSequencedType( ) ? 'true' : 'false' ) . '</li>';
				$str .= self::strIdent( $tabs ) .  '<li><font color="#000066">many:</font> ' . ( $property->isMany( ) ? 'true' : 'false' ) . '</li>';
				$str .= self::strIdent( $tabs ) .  '<li><font color="#000066">readonly:</font> ' . ( $property->isReadOnly( ) ? 'true' : 'false' ) . '</li>';
				$str .= self::strIdent( $tabs ) .  '<li><font color="#000066">default:</font> ' . self::dumpVar( $property->getDefault( ), true ) . '</li>';
				$str .= self::strIdent( $tabs ) .  '</ul>';
			}
			
			$str .= self::strIdent( --$tabs ) . '</ul>' . 
					self::strIdent( $tabs ) . '</li>'; 
		}
		
		$str .= self::strIdent( $tabs - 1 ) . '</li>';
		$vars = array( );
		if( $magicMethod )	{
			$str .= self::strIdent( $tabs ) . '<li>Magic';
			foreach( $obj as $key => $value )	{
				$strProp = '<font color="#006600"><i>public</i></font> ' . $key . ' <font color="#888a85">=&gt;</font> ';
				$str .= self::strIdent( $tabs + 1 ) . '<ul><li>' . $strProp . self::strIdent( $tabs + 2 ) . '<ul><li>' . self::dumpVar( $value, true ) . '</li></ul>' . self::strIdent( $tabs + 1 ) . '</li></ul>';
			}
			$str .= self::strIdent( $tabs ) . '</li>';
		}
		$str .= self::strIdent( $tabs ) . '</ul>';
		if( !$return && \dayscript\log\LogManager::canLog( \dayscript\log\LogManager::LOG_USER_INFO ) )	{
			echo $str;
			return null;
		}
		return $str;
	}
	
	public static function strIdent( $tabs = 0 )	{
		return '';//"\n" . \str_repeat( "\t", $tabs );
	} 
}
